/*
 * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 * The software in this package is published under the terms of the CPAL v1.0
 * license, a copy of which has been included with this distribution in the
 * LICENSE.txt file.
 */
package org.mule.runtime.config.spring.parsers.processors;

import org.mule.runtime.config.spring.MuleHierarchicalBeanDefinitionParserDelegate;
import org.mule.runtime.config.spring.parsers.PostProcessor;
import org.mule.runtime.config.spring.parsers.assembly.BeanAssembler;
import org.mule.runtime.config.spring.parsers.assembly.BeanAssemblerFactory;
import org.mule.runtime.config.spring.parsers.assembly.configuration.PropertyConfiguration;
import org.mule.runtime.config.spring.util.SpringXMLUtils;

import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * <p>
 * This iterates over child elements, parsing them and calling {@link #insertBean(BeanAssembler, Object, Element, Element)}.
 * </p>
 * <p>
 * There are two ways we can parse a tree of elements - have an external loop or let each parser iterate over its own children.
 * Mule uses the first strategy, but some (most? all?) third party BDPs use the second. This processor lets us use third party
 * beans inside the Mule framework.
 * </p>
 * <p>
 * So this is a very specialised parser that should only be used when trying to inter-operate with beans from third party packages
 * which themselves control how their children are parsed.
 * </p>
 * <p>
 * Since for Mule beans the iteration over child elements (at least currently) is done via
 * {@link MuleHierarchicalBeanDefinitionParserDelegate} the calling parser needs to set the flag
 * {@link MuleHierarchicalBeanDefinitionParserDelegate#MULE_NO_RECURSE} - this stops the Mule recursion from working.
 * </p>
 * <p>
 * NOTE - IMHO (ac) the Mule approach was probably a mistake; this processor could be used as a way to slowly migrate the Mule
 * code to the more common approach.
 * </p>
 */
public abstract class AbstractChildElementIterator implements PostProcessor {

  private BeanAssemblerFactory beanAssemblerFactory;
  private PropertyConfiguration configuration;

  public AbstractChildElementIterator(BeanAssemblerFactory beanAssemblerFactory, PropertyConfiguration configuration) {
    this.beanAssemblerFactory = beanAssemblerFactory;
    this.configuration = configuration;
  }

  public void postProcess(ParserContext context, BeanAssembler assembler, Element element) {
    NodeList children = element.getChildNodes();
    for (int i = 0; i < children.getLength(); ++i) {
      Node child = children.item(i);
      if (child.getNodeType() == Node.ELEMENT_NODE) {
        processChildElement(context, assembler, element, (Element) child);
      }
    }
  }

  protected void processChildElement(ParserContext context, BeanAssembler assembler, Element parent, Element child) {
    Object childBean = null;
    if (SpringXMLUtils.isBeansNamespace(child) || SpringXMLUtils.isLocalName(child, BeanDefinitionParserDelegate.REF_ELEMENT)) {
      childBean = context.getDelegate().parsePropertySubElement(child, null);
    } else {
      childBean = context.getDelegate().parseCustomElement(child, assembler.getBean().getBeanDefinition());
    }
    BeanAssembler targetAssembler =
        beanAssemblerFactory.newBeanAssembler(null, null, configuration, assembler.getBean().getRawBeanDefinition());
    insertBean(targetAssembler, childBean, parent, child);
  }

  protected abstract void insertBean(BeanAssembler targetAssembler, Object childBean, Element parent, Element child);

}
